<template>
  <div class="app">
    <div @scroll="onScroll" :style="{ height: CONTAINER_HEIGHT + 'px' }" class="container">
      <div :style="{ height: state.pillarHeight + 'px' }" class="pillar" />

      <div :style="{ top: state.offsetTop + 'px' }" class="view">
        <div :style="{ height: height + 'px' }" class="view-item" v-for="item in viewList" :key="item">
          {{ item }}
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { computed, onMounted } from "vue";
import { reactive } from "vue";
const CONTAINER_HEIGHT = 300;
const BUFFER = 3;

const { height, list } = defineProps<{
  height: number;
  list: any[];
}>();

const state = reactive({
  startIndex: 0,
  endIndex: 0,
  pillarHeight: 0,
  viewCount: 0,
  offsetTop: 0,
});

const viewList = computed(() => list.slice(state.startIndex, state.endIndex));

function onScroll(e: any) {
  const scrollTop = (e.target as HTMLElement).scrollTop;
  const sIdx = Math.floor(scrollTop / height);
  state.startIndex = sIdx < BUFFER ? 0 : sIdx - BUFFER;
  state.endIndex = Math.min(state.startIndex + BUFFER * 2 + state.viewCount, list.length);

  state.offsetTop = state.startIndex * height;
}

onMounted(() => {
  state.viewCount = Math.ceil(CONTAINER_HEIGHT / height);
  state.pillarHeight = list.length * height;

  state.endIndex = state.viewCount + BUFFER;
});
</script>

<style>
.container {
  width: 200px;
  overflow: auto;
  position: relative;
  background-color: red;
}

.view {
  width: 100%;
  position: absolute;
  top: 0;
  left: 0;
  height: fit-content;
}

.view-item {
  width: 100%;
  background-color: rgb(206, 157, 255);
}
</style>
